#ifndef GMACHINE_HEADER_GFUNCTION_H
#define GMACHINE_HEADER_GFUNCTION_H

extern "C"
{
	#include "lua/lua.h"
	#include "lua/lualib.h"
	#include "lua/lauxlib.h"
}

namespace base
{
	class LuaMachine
	{
	public:
		LuaMachine(): mLuaVM(NULL)
		{
		}

		~LuaMachine()
		{
		}
	public:
		bool Initialize()
		{
			if (mLuaVM == NULL)
			{
				const luaL_Reg lualibs[] = {
					{LUA_COLIBNAME, luaopen_base},
					{LUA_LOADLIBNAME, luaopen_package},
					{LUA_TABLIBNAME, luaopen_table},
					{LUA_IOLIBNAME, luaopen_io},
					{LUA_OSLIBNAME, luaopen_os},
					{LUA_STRLIBNAME, luaopen_string},
					{LUA_MATHLIBNAME, luaopen_math},
					{LUA_DBLIBNAME, luaopen_debug},
					{NULL, NULL}
				};

				mLuaVM = luaL_newstate();

				if (mLuaVM != NULL)
				{
					const luaL_Reg *lib = lualibs;
					for (; lib->func; lib++) {
						lua_pushcfunction(mLuaVM, lib->func);
						lua_pushstring(mLuaVM, lib->name);
						lua_call(mLuaVM, 1, 0);
					}
				}
			}

			if (mLuaVM == NULL)
			{
				return false;
			}
			return true;
		}

		void Terminate()
		{
			if (mLuaVM)
			{
				lua_close(mLuaVM);
				mLuaVM = NULL;
			}
		}

	public:

	public:
		lua_State *	mLuaVM;
	};


	namespace luabind
	{
		typedef LUA_INTEGER		IntegerObject;
		typedef LUA_NUMBER		FloatingObject;
		typedef char *			StringObject;
		typedef void *			HandleObject;
	
		typedef unsigned char U8;

		/////////////////////////////////////////////////////////////////////
		// Function: PickInteger, PickFloating, PickString, PickHandle
		/////////////////////////////////////////////////////////////////////
		// PickInteger:
		inline IntegerObject* PickInteger(lua_State* state)
		{	
			return (IntegerObject*)lua_topointer(state, 0);
		}

		// PickFloating:
		inline FloatingObject* PickFloating(lua_State* state)
		{
			return (FloatingObject*)lua_topointer(state, 0);
		}

		// PickString:
		inline StringObject* PickString(lua_State* state)
		{
			return (StringObject*)lua_topointer(state, 0);
		}

		// PickHandle:
		inline HandleObject* PickHandle(lua_State* state)
		{
			return (HandleObject*)lua_topointer(state, 0);
		}

		/////////////////////////////////////////////////////////////////////
		// Class: ClassObject
		/////////////////////////////////////////////////////////////////////
		class ClassObject
		{
		public:
			ClassObject(HandleObject*(*c)(), void(*d)(HandleObject*)):constructor(c), destructor(d) { return; }
			ClassObject(const ClassObject& rhs):constructor(rhs.constructor), destructor(rhs.destructor) { return; }
		public:
			ClassObject& operator=(const ClassObject& rhs)
			{
				if (this != &rhs)
				{
					this->constructor	=	rhs.constructor;
					this->destructor	=	rhs.destructor;
				}
				return *this;
			}
		public:
			HandleObject * Create() const {  return constructor(); }
			void Delete(HandleObject * obj) const { destructor(obj); }
		private:
			HandleObject * (*constructor)(); void (*destructor)(HandleObject *);
		};

		/////////////////////////////////////////////////////////////////////
		// Template: ExportClass
		// Inherited from ClassObject.
		/////////////////////////////////////////////////////////////////////
		template<typename T> 
		class ExportClass:public ClassObject {
			static HandleObject * Constructor() {
				return (HandleObject*)(new T());
			}
			static void Destructor(HandleObject * obj) {
				delete (T*)(obj);
			}
		public:	
			ExportClass<T>():ClassObject(ExportClass<T>::Constructor, ExportClass<T>::Destructor){ return; }
		};


		/////////////////////////////////////////////////////////////////////
		// Class: FunctionObject
		/////////////////////////////////////////////////////////////////////
		class FunctionObject
		{
		public:
			FunctionObject(void (*c)(void *, lua_State *), void * f):caller(c), callee(f) { return; }
			FunctionObject(const FunctionObject& rhs):caller(rhs.caller), callee(rhs.callee) { return; }
		public:
			FunctionObject& operator=(const FunctionObject& rhs)
			{
				if (this != &rhs)
				{
					this->caller = rhs.caller;
					this->callee = rhs.callee;
				}
				return *this;
			}
		public:
			void Invoke(lua_State * state) const
			{
				caller(callee, state);
			}
		private:
			void (*caller)(void *, lua_State *); void * callee;
		};
	
	
		/////////////////////////////////////////////////////////////////////
		// Template Function: Objectval
		/////////////////////////////////////////////////////////////////////
		template<typename T> struct Objectval
		{
			static inline T* Read(lua_State* state)
			{
				return (T*)(PickHandle(state));
			}
		};

		/////////////////////////////////////////////////////////////////////
		// Template Function: Returnval
		/////////////////////////////////////////////////////////////////////
		template<typename T> struct Returnval
		{
			static inline T* Read(lua_State* state)
			{
				return (T*)(PickHandle(state));
			}
		};

		template<typename T> struct Returnval<T&>
		{
			static inline T* Read(lua_State* state)
			{
				return (T*)(PickHandle(state));
			}
		};

#		define SPECITYMETHOD(method, type, ret)						\
		template<> struct Returnval<type>							\
		{															\
			static inline ret * Read(lua_State* state)				\
			{														\
				return method(state);								\
			}														\
		}

		SPECITYMETHOD(PickFloating, float, FloatingObject);
		SPECITYMETHOD(PickFloating, double, FloatingObject);
		SPECITYMETHOD(PickFloating, long double, FloatingObject);
		SPECITYMETHOD(PickInteger, bool, IntegerObject);
		SPECITYMETHOD(PickInteger, char, IntegerObject);
		SPECITYMETHOD(PickInteger, signed char, IntegerObject);
		SPECITYMETHOD(PickInteger, unsigned char, IntegerObject);
		SPECITYMETHOD(PickInteger, int, IntegerObject);
		SPECITYMETHOD(PickInteger, short int, IntegerObject);
		SPECITYMETHOD(PickInteger, long int, IntegerObject);
		SPECITYMETHOD(PickInteger, unsigned int, IntegerObject);
		SPECITYMETHOD(PickInteger, unsigned short int, IntegerObject);
		SPECITYMETHOD(PickInteger, unsigned long int, IntegerObject);
#		if defined(SUPPORT_wchar_t)
		SPECITYMETHOD(PickInteger, wchar_t, IntegerObject);
#		endif
#		if defined(SUPPORT_long_long_int)
		SPECITYMETHOD(PickInteger, long long int, IntegerObject);
		SPECITYMETHOD(PickInteger, unsigned long long int, IntegerObject);
#		endif
#		undef  SPECITYMETHOD

		/////////////////////////////////////////////////////////////////////
		// Template Function: Parameter
		/////////////////////////////////////////////////////////////////////
		template<typename T> struct Parameter
		{
			static inline T Read(lua_State* state)
			{
				return (*(T*)PickHandle(state));
			}
		};

		template<typename T> struct Parameter<T&>
		{
			static inline T& Read(lua_State* state)
			{
				return (T&)(**(T**)PickHandle(state));
			}
		};

		template<> struct Parameter<bool>
		{
			static inline bool Read(lua_State* state)
			{
				return (*PickInteger(state) != 0);
			}
		};

#		define SPECITYMETHOD(method, type)							\
		template<> struct Parameter< type >							\
		{															\
			static inline type Read(lua_State* state)				\
			{														\
				return type(*method(state));						\
			}														\
		}

		SPECITYMETHOD(PickFloating, float);
		SPECITYMETHOD(PickFloating, double);
		SPECITYMETHOD(PickFloating, long double);
		SPECITYMETHOD(PickInteger, char);
		SPECITYMETHOD(PickInteger, signed char);
		SPECITYMETHOD(PickInteger, unsigned char);
		SPECITYMETHOD(PickInteger, int);
		SPECITYMETHOD(PickInteger, short int);
		SPECITYMETHOD(PickInteger, long int);
		SPECITYMETHOD(PickInteger, unsigned int);
		SPECITYMETHOD(PickInteger, unsigned short int);
		SPECITYMETHOD(PickInteger, unsigned long int);
#		if defined(SUPPORT_wchar_t)
		SPECITYMETHOD(PickInteger, wchar_t);
#		endif
#		if defined(SUPPORT_long_long_int)
		SPECITYMETHOD(PickInteger, long long int);
		SPECITYMETHOD(PickInteger, unsigned long long int);
#		endif
#		undef  SPECITYMETHOD


		/////////////////////////////////////////////////////////////////////
		// Template: ExportFunction, for member functions.
		// Return: FunctionObject
		/////////////////////////////////////////////////////////////////////
		namespace ImplMemberFunctionCaller
		{
			//--------------------------------------------------------------------------
			// 16 params, have a return value.
			template<typename TRET, typename TCLASS, 
				typename P1 = void, typename P2 = void, typename P3 = void, typename P4 = void, 
				typename P5 = void, typename P6 = void, typename P7 = void, typename P8 = void,
				typename P9 = void, typename P10= void, typename P11= void, typename P12= void,
				typename P13= void, typename P14= void, typename P15= void, typename P16= void>
			struct Caller
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					TRET(TCLASS::*method)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16)
						= *(TRET(TCLASS::**)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16))(&callee);
					*(Returnval<TRET>::Read(state)) = (Objectval<TCLASS>::Read(state)->*method)(
						Parameter<P1>::Read(state), Parameter<P2>::Read(state),
						Parameter<P3>::Read(state), Parameter<P4>::Read(state),
						Parameter<P5>::Read(state), Parameter<P6>::Read(state),
						Parameter<P7>::Read(state), Parameter<P8>::Read(state),
						Parameter<P9>::Read(state), Parameter<P10>::Read(state),
						Parameter<P11>::Read(state),Parameter<P12>::Read(state),
						Parameter<P13>::Read(state),Parameter<P14>::Read(state),
						Parameter<P15>::Read(state),Parameter<P16>::Read(state));
					return;
				}
			};

			// 16 params, no return value.
			template<typename TCLASS, 
				typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8,
				typename P9, typename P10,typename P11,typename P12,typename P13,typename P14,typename P15,typename P16>
			struct Caller<void, TCLASS, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					void(TCLASS::*method)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) 
						= *(void(TCLASS::**)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16))(&callee);
					Objectval<TCLASS>::Read(state)->*method( 
						Parameter<P1>::Read(state), Parameter<P2>::Read(state),
						Parameter<P3>::Read(state), Parameter<P4>::Read(state),
						Parameter<P5>::Read(state), Parameter<P6>::Read(state),
						Parameter<P7>::Read(state), Parameter<P8>::Read(state),
						Parameter<P9>::Read(state), Parameter<P10>::Read(state),
						Parameter<P11>::Read(state),Parameter<P12>::Read(state),
						Parameter<P13>::Read(state),Parameter<P14>::Read(state),
						Parameter<P15>::Read(state),Parameter<P16>::Read(state));
					return;
				}
			};

			//--------------------------------------------------------------------------
			// 2 params, have a return value.
			template<typename TRET, typename TCLASS, typename P1, typename P2>
			struct Caller<TRET, TCLASS, P1, P2, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state) 
				{
					TRET(TCLASS::*method)(P1, P2) = *(TRET(TCLASS::**)(P1, P2))(&callee);
					*(Returnval<TRET>::Read(state)) = (Objectval<TCLASS>::Read(state)->*method)(
						Parameter<P1>::Read(state), Parameter<P2>::Read(state));
					return;
				}
			};
			// 2 params, no return value.
			template<typename TCLASS, typename P1, typename P2>
			struct Caller<void, TCLASS, P1, P2, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					void(TCLASS::*method)(P1, P2) = *(void(TCLASS::**)(P1, P2))(&callee);
					(Objectval<TCLASS>::Read(state)->*method)(Parameter<P1>::Read(state), Parameter<P2>::Read(state));
					return;
				}
			};
			//--------------------------------------------------------------------------
			// 1 param, have a return value.
			template<typename TRET, typename TCLASS, typename P1>
			struct Caller<TRET, TCLASS, P1, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					TRET(TCLASS::*method)(P1) = *(TRET(TCLASS::**)(P1))(&callee);
					*(Returnval<TRET>::Read(state)) = (Objectval<TCLASS>::Read(state)->*method)(Parameter<P1>::Read(state));
					return;
				}
			};
			// 1 param, no return value.
			template<typename TCLASS, typename P1>
			struct Caller<void, TCLASS, P1, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					void(TCLASS::*method)(P1) = *(void(TCLASS::**)(P1))(&callee);
					(Objectval<TCLASS>::Read(state)->*method)(Parameter<P1>::Read(state));
					return;
				}
			};

			//--------------------------------------------------------------------------
			// no param, have a return value.
			template<typename TRET, typename TCLASS>
			struct Caller<TRET, TCLASS, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					TRET(TCLASS::*method)() = *(TRET(TCLASS::**)())(&callee);
					*(Returnval<TRET>::Read(state)) = (Objectval<TCLASS>::Read(state)->*method)();
					return;
				}
			};
			// no param, no return value.
			template<typename TCLASS>
			struct Caller<void, TCLASS, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					void(TCLASS::*method)() = *(void(TCLASS::**)())(&callee);
					(Objectval<TCLASS>::Read(state)->*method());	
					return;
				}
			};
			//--------------------------------------------------------------------------
		}

		template<typename TRET, typename TCLASS, typename P1, typename P2>
		inline FunctionObject ExportFunction(TRET(TCLASS::*f)(P1, P2))
		{
			return FunctionObject(ImplMemberFunctionCaller::Caller<TRET, TCLASS, P1, P2>::Invoke, (void*)&f);
		}

		template<typename TRET, typename TCLASS, typename P1>
		inline FunctionObject ExportFunction(TRET(TCLASS::*f)(P1))
		{
			return FunctionObject(ImplMemberFunctionCaller::Caller<TRET, TCLASS, P1>::Invoke, (void*)&f);
		}

		template<typename TRET, typename TCLASS>
		inline FunctionObject ExportFunction(TRET(TCLASS::*f)())
		{
			return FunctionObject(ImplMemberFunctionCaller::Caller<TRET, TCLASS>::Invoke, (void*)&f);
		}

		/////////////////////////////////////////////////////////////////////
		// Template: ExportFunction, for global functions.
		// Return: FunctionObject
		/////////////////////////////////////////////////////////////////////
#		define CALLCONV __cdecl
#		define CALLERNS ImplGlobalFunctionCaller_cdecl
		/////////////////////////////////////////////////////////////////////
		//  This file is a part of "GFunction.h", not a stand-alone file.
		//	Do NOT directly include this file, just include "GFunction.h".
		/////////////////////////////////////////////////////////////////////
		namespace CALLERNS
		{
			//-----------------------------------------------------
			// 16 params
			template<typename TRET, 
				typename P1 = void, typename P2 = void, typename P3 = void, typename P4 = void,
				typename P5 = void, typename P6 = void, typename P7 = void, typename P8 = void,
				typename P9 = void, typename P10= void, typename P11= void, typename P12= void,
				typename P13= void, typename P14= void, typename P15= void, typename P16= void>
			struct Caller
			{	
				static inline void Invoke(void * callee, lua_State* state) 
				{
					*(Returnval<TRET>::Read(state)) = (*((TRET(CALLCONV **)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16))&callee))(
						Parameter<P1>::Read(state), Parameter<P2>::Read(state),
						Parameter<P3>::Read(state), Parameter<P4>::Read(state),
						Parameter<P5>::Read(state), Parameter<P6>::Read(state),
						Parameter<P7>::Read(state), Parameter<P8>::Read(state),
						Parameter<P9>::Read(state), Parameter<P10>::Read(state),
						Parameter<P11>::Read(state),Parameter<P12>::Read(state),
						Parameter<P13>::Read(state),Parameter<P14>::Read(state),
						Parameter<P15>::Read(state),Parameter<P16>::Read(state));
					return;
				}
			};
			// no return value
			template<
				typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8,
				typename P9, typename P10,typename P11,typename P12,typename P13,typename P14,typename P15,typename P16> 
			struct Caller<void, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					(*((void(CALLCONV **)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16))&callee))(
						Parameter<P1>::Read(state), Parameter<P2>::Read(state),
						Parameter<P3>::Read(state), Parameter<P4>::Read(state),
						Parameter<P5>::Read(state), Parameter<P6>::Read(state),
						Parameter<P7>::Read(state), Parameter<P8>::Read(state),
						Parameter<P9>::Read(state), Parameter<P10>::Read(state),
						Parameter<P11>::Read(state),Parameter<P12>::Read(state),
						Parameter<P13>::Read(state),Parameter<P14>::Read(state),
						Parameter<P15>::Read(state),Parameter<P16>::Read(state));
					return;
				}
			};

			//-----------------------------------------------------
			// 2 params
			template<typename TRET, typename P1, typename P2>
			struct Caller<TRET, P1, P2, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					*(Returnval<TRET>::Read(state)) = (*((TRET(CALLCONV **)(P1, P2))&callee))(Parameter<P1>::Read(state), Parameter<P2>::Read(state));
					return;
				}
			};
			// no return value
			template<typename P1, typename P2> 
			struct Caller<void, P1, P2, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					(*((void(CALLCONV **)(P1, P2))&callee))(Parameter<P1>::Read(state), Parameter<P2>::Read(state));
					return;
				}
			};
			//-----------------------------------------------------
			// 1 params
			template<typename TRET, typename P1> 
			struct Caller<TRET, P1, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					*(Returnval<TRET>::Read(state)) = (*((TRET(CALLCONV **)(P1))&callee))(Parameter<P1>::Read(state));
					return;
				}
			};
			// no return value
			template<typename P1> 
			struct Caller<void, P1, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					(*((void(CALLCONV **)(P1))&callee))(Parameter<P1>::Read(state));
					return;
				}
			};

			//-----------------------------------------------------
			// no param
			template<typename TRET> 
			struct Caller<TRET, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					*(Returnval<TRET>::Read(state)) = (*((TRET(CALLCONV **)())&callee))();
					return;
				}
			};
			// no return value
			template<> 
			struct Caller<void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void>
			{
				static inline void Invoke(void * callee, lua_State* state)
				{
					(*((void(CALLCONV **)())&callee))();
					return;
				}
			};
			//-----------------------------------------------------
		}

		/////////////////////////////////////////////////////////////////////
		// Template: ExportFunction, for global functions, which have a return value.
		// Return: FunctionObject
		/////////////////////////////////////////////////////////////////////

		template<typename TRET, typename P1, typename P2> 
		inline FunctionObject ExportFunction(TRET(CALLCONV * f)(P1, P2)) 
		{ 
			return FunctionObject(CALLERNS::Caller<TRET, P1, P2>::Invoke, *(void**)&f);
		}

		template<typename TRET, typename P1> 
		inline FunctionObject ExportFunction(TRET(CALLCONV * f)(P1))
		{ 
			return FunctionObject(CALLERNS::Caller<TRET, P1>::Invoke, *(void**)&f);
		}

		template<typename TRET> 
		inline FunctionObject ExportFunction(TRET(CALLCONV * f)(void)) 
		{ 
			return FunctionObject(CALLERNS::Caller<TRET>::Invoke, *(void**)&f);
		}
#		undef CALLERNS
#		undef CALLCONV
	}
}

#endif	//ifndef GMACHINE_HEADER_GFUNCTION_H
